#include "bfont_renderer.h"
#include "teflon/core/asset.h"
#include <vector>

namespace teflon
{
    BFontRenderer::BFontRenderer()
    {
        std::map<teflon::ShaderType, std::string> shader_sources;
        std::string vspath = get_asset_path("shaders/bfont.vs");
        std::string fspath = get_asset_path("shaders/bfont.fs");
        shader_sources.insert(std::make_pair(teflon::ShaderType::Vertex, vspath));
        shader_sources.insert(std::make_pair(teflon::ShaderType::Fragment, fspath));
        shader = std::make_shared<ShaderProgram>(shader_sources);

        // create vao
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);

        glGenBuffers(1, &vbo_position);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
        glBufferData(GL_ARRAY_BUFFER, MAX_CHARACTERS * sizeof(glm::vec3), nullptr, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);
        glEnableVertexAttribArray(0);

        glGenBuffers(1, &vbo_uv);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
        glBufferData(GL_ARRAY_BUFFER, MAX_CHARACTERS * sizeof(glm::vec2), nullptr, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), (void*)0);
        glEnableVertexAttribArray(1);

        glGenBuffers(1, &ibo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_CHARACTERS * sizeof(uint16_t), nullptr, GL_DYNAMIC_DRAW);
        glBindVertexArray(0);
    }

    void BFontRenderer::Begin(const std::string& fontpath)
    {
        font.Init(fontpath);

        glEnable(GL_CULL_FACE);
        glFrontFace(GL_CW);

        glDepthMask(GL_TRUE);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        shader->Bind();
        font.Bind();
    }

    void BFontRenderer::End()
    {
        // do nothing.
        font.Unbind();
    }

    void BFontRenderer::Draw(const std::string& text, float fontscale)
    {
        std::vector<glm::vec3> positions;
        std::vector<glm::vec2> uvs;
        std::vector<uint16_t> indices;
        font.MapVertex(text, positions, uvs, indices, fontscale);

        glBindVertexArray(vao);

        glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
        glBufferSubData(GL_ARRAY_BUFFER, 0, positions.size() * sizeof(glm::vec3), positions.data());

        glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
        glBufferSubData(GL_ARRAY_BUFFER, 0, uvs.size() * sizeof(glm::vec2), uvs.data());

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indices.size() * sizeof(uint16_t), indices.data());
#if 1
        glBindTexture(GL_TEXTURE_2D, font.GetTextureId());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
        glActiveTexture(GL_TEXTURE0);
        shader->SetTextureUniform("mainTex", 0);
#endif
        glDrawElements(GL_TRIANGLES, int(indices.size()), GL_UNSIGNED_SHORT, nullptr);
    }
}
